package com.dong.sqltoy.tenant;

import lombok.extern.slf4j.Slf4j;
import org.sagacity.sqltoy.config.annotation.Entity;
import org.sagacity.sqltoy.config.model.OperateType;
import org.sagacity.sqltoy.config.model.SqlToyResult;
import org.sagacity.sqltoy.model.IgnoreCaseSet;
import org.sagacity.sqltoy.plugins.IUnifyFieldsHandler;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author dengxiudong
 * @Date 2025/9/25 16:15
 */
@Slf4j
public class UnifyFieldsAutoFillHandler implements IUnifyFieldsHandler
{
    private Set<String> ignoreTables;
    
    // 添加无参构造函数
    public UnifyFieldsAutoFillHandler() {
        this.ignoreTables = new HashSet<>();
    }
    
    public UnifyFieldsAutoFillHandler(Set<String> ignoreTables)
    {
        this.ignoreTables = ignoreTables;
    }

    // ------------- 请仔细阅读改接口里面的说明 ------------------------------------/

    // 怎么获取当前用户的信息? 举一个简单例子:通过filter将当前用户信息放入ThreadLocal中，然后这里就可以获取到

    /**
     * @return
     * @TODO 设置创建记录时需要赋值的字段和对应的值(弹性模式 : 即优先以传递的值优先 ， 为null再填充)
     */
    public Map<String, Object> createUnifyFields()
    {
        // 范例
        var map = new HashMap<String, Object>();
        // LocalDateTime nowDate = DateUtil.getDateTime();
        // Timestamp nowTime = DateUtil.getTimestamp(null);
        
        // 获取当前用户信息（实际项目中应该从SecurityContext或ThreadLocal中获取）
        String currentUser = getCurrentUser();
        
        // 获取当前用户的租户ID
        String currentTenantId = getCurrentUserTenantId();
        
        // 不存在的字段名称会自动忽略掉(因此下述属性未必是每个表中必须存在的)
        // 插入时自动设置creator字段
        map.put("creator", currentUser);
        // map.put("createTime", nowTime);
        
        // 插入时也设置updater字段（首次创建时creator和updater相同）
        map.put("updater", currentUser);
        // map.put("updateTime", nowTime);
        
        // 插入时自动设置租户ID字段（使用数据库字段名）
        // map.put("tenant_id", currentTenantId);
        // 同时也设置Java字段名，以防万一
        map.put("tenantId", currentTenantId);
        
        // // enabled 是否启用状态
        // map.put("enabled", 1);
        return map;
    }


    // 在非强制情况下，create和update赋值都是先判断字段是否已经赋值，如已经赋值则忽视
    // 强制赋值后，则忽视字段赋值，强制覆盖

    /**
     * @return
     * @TODO 設置更新记录时需要赋值的字段和对应的值
     */
    public Map<String, Object> updateUnifyFields()
    {
        var map = new HashMap<String, Object>();
        
        // 获取当前用户信息（实际项目中应该从SecurityContext或ThreadLocal中获取）
        String currentUser = getCurrentUser();
        
        // 更新时自动设置updater字段
        map.put("updater", currentUser);
        // map.put("updateTime", DateUtil.getTimestamp(null));
        
        return map;
    }

    /**
     * @return
     * @TODO 強制修改的字段(一般针对updateTime属性)
     */
    public IgnoreCaseSet forceUpdateFields()
    {
        var set = new IgnoreCaseSet();
        // 强制更新updater字段，即使该字段已有值也会被覆盖
        set.add("updater");
        // set.add("updateTime");
        return set;
    }

    // --- 参考:org.sagacity.sqltoy.plugins.interceptors.TenantFilterInterceptor ---/
    // 如果启用SqlInterceptors:xxxx.TenantInterceptor
    // sql查询租户隔离，建议要实现此方法，因为在分页缓存count环节需要此值作为key的组成

    /**
     * @param entityClass 可以为null
     * @param operType    可以为null
     * @return
     * @TODO 获取授权租户信息，传递表名和操作类型目的为程序可以控制返回:所在租户和授权租户 提供部分决策依据
     * 一般你可以直接返回当前用户的授权租户id数组，主要用于SqlInterceptors，如自定义的TenantInterceptor
     * 简单实现
     */
    public String[] authTenants(Class entityClass, OperateType operType)
    {
        // 你可以不用管entityClass、operType参数，直接返回当前用户授权的租户Id
        // 怎么获取? 通过filter将用户信息放入ThreadLocal，这里就随意获取了,请根据情况发挥
        // return getCurrentUserAuthedTenants();
        if(ignoreTable(entityClass)){
            return null;
        }else{
            return new String[]{"888999"};
        }
    }

    /**
     * 返回当前用户的租户id
     *
     * @return
     */
    public String getUserTenantId()
    {
        return "888999";
    }


    public boolean ignoreTable(Class entityClass)
    {
        String tableName = null;

        // 检查实体类是否有@Entity注解
        if (entityClass.isAnnotationPresent(Entity.class)) {
            Entity entityAnnotation = (Entity) entityClass.getAnnotation(Entity.class);
            tableName = entityAnnotation.tableName();
        }

        // 如果没有@Entity注解，可以尝试使用类名作为默认表名
        if (tableName == null || tableName.isEmpty()) {
            // 将类名转换为下划线命名（例如：UserInfo -> user_info）
            tableName = camelToUnderline(entityClass.getSimpleName());
        }

        // 使用表名进行后续操作
        System.out.println("当前操作的表名：" + tableName);
        return ignoreTables.contains(tableName);
    }

    /**
     * 获取当前用户的租户ID
     * 实际项目中应该从用户上下文或认证信息中获取
     * @return 当前用户的租户ID
     */
    private String getCurrentUserTenantId() {
        // TODO: 实际项目中需要实现真正的租户ID获取逻辑
        // 例如：
        // UserContext userContext = UserContextHolder.getCurrentUser();
        // return userContext != null ? userContext.getTenantId() : null;
        
        // 或者从JWT Token中获取：
        // String token = JwtUtils.getTokenFromRequest();
        // return JwtUtils.getTenantIdFromToken(token);
        
        // 或者从Spring Security中获取：
        // Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // if (auth instanceof JwtAuthenticationToken) {
        //     return ((JwtAuthenticationToken) auth).getToken().getClaimAsString("tenantId");
        // }
        
        // 这里仅作示例，返回固定值
        return "888999";
    }

    /**
     * 获取当前用户信息
     * 实际项目中应该从SecurityContext、JWT Token或ThreadLocal中获取当前登录用户
     * @return 当前用户标识
     */
    private String getCurrentUser() {
        // TODO: 实际项目中需要实现真正的用户获取逻辑
        // 例如：
        // Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // return auth != null ? auth.getName() : "system";
        
        // 或者从ThreadLocal中获取：
        // return UserContextHolder.getCurrentUser();
        
        // 这里仅作示例，返回固定值
        return "东方不败";
    }

    // 驼峰命名转下划线命名的工具方法
    private String camelToUnderline(String camelCaseName)
    {
        if (camelCaseName == null || camelCaseName.isEmpty()) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        result.append(camelCaseName.charAt(0));
        for (int i = 1; i < camelCaseName.length(); i++) {
            char ch = camelCaseName.charAt(i);
            if (Character.isUpperCase(ch)) {
                result.append("_");
                result.append(Character.toLowerCase(ch));
            } else {
                result.append(ch);
            }
        }
        return result.toString();
    }
}